/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_generators.cpp - ...
//

// TODO:
// - Rewrite pretty much all of this to support the editor interface...


#include "r_local.h"
#include "../server/server.h"


#undef DELETE
#undef NEW

#define NEW(type)				((type*)calloc(1, sizeof(type)))
#define NEW_ARRAY(type, num)	((type*)calloc(num, sizeof(type)))
#define DELETE(var)				(free(var))
#define DELETE_ARRAY(var)		(free(var))

#define VectorCopyRound(in, out) (((out)[0] = (int)(in)[0]), ((out)[1] = (int)(in)[1]), (out)[2] = (int)(in)[2])

#define lerp(start,end,frac,out) \
			((out)[0] = (start)[0] + (frac) * ((end[0]) - (start[0])), \
			(out)[1] = (start)[1] + (frac) * ((end[1]) - (start[1])), \
			(out)[2] = (start)[2] + (frac) * ((end[2]) - (start[2])))

//================================================================================================================
// Structures/Types

enum generatorType
{
	GT_None,
	GT_Box,
	GT_Cylinder,
	GT_Square,
	GT_Circle
};

struct pointList_t
{
	pointList_t *next;
	pointList_t *prev;

	vec3_t point;
};

struct generator_t
{
	generatorType type;
	vec3_t center;

	union
	{
		struct
		{
			vec3_t size;
		} bbox;

		struct
		{
			float radius;
			float dummy;
			float height;
		} bcylinder;

		struct
		{
			float radius;
			float dummy1;
			float dummy2;
		} bcircle;

		struct
		{
			vec2_t size;
			float dummy;
		} bsquare;
	};

	pointList_t *points;
	struct generator_t *next;
};

void delete_generator (generator_t *generator);

generatorList_t generators;

void init_generatorList (generatorList_t *list)
{
	memset(list, 0, sizeof(list));
}

void clear_generators();

void delete_generatorList (generatorList_t *list)
{
	if (list->list != NULL)
		clear_generators();

	list->count = 0;
	list->inEditorMode = false;
	list->selected = NULL;
}

void link_generator (generator_t *generator);

void create_generator (generator_t *generator)
{
	memset(generator, 0, sizeof(generator_t));
	link_generator(generator);
}

void create_cylinder_generator (generator_t *generator, vec3_t center, float radius, float height)
{
	generator->type = GT_Cylinder;
	VectorCopy(center, generator->center);
	generator->bcylinder.radius = radius;
	generator->bcylinder.height = height;
}

void create_circle_generator (generator_t *generator, vec3_t center, float radius)
{
	generator->type = GT_Circle;
	VectorCopy(center, generator->center);
	generator->bcircle.radius = radius;
}

void create_square_generator (generator_t *generator, vec3_t center, vec2_t size)
{
	generator->type = GT_Square;
	VectorCopy(center, generator->center);
	memcpy(generator->bsquare.size, size, sizeof(size) * 2);
}

void create_box_generator (generator_t *generator, vec3_t center, vec3_t size)
{
	generator->type = GT_Box;
	VectorCopy(center, generator->center);
	VectorCopy(size, generator->bbox.size);
}

void delete_generator (generator_t *generator)
{
	if (generator->points != NULL)
	{
		pointList_t *point;
		for (point = generator->points; point != NULL; )
		{
			pointList_t *next = point->next;

			free(point);

			point = next;
		}
	}

	if (generator == generators.list)
		generators.list = generator->next;
	else
	{
		generator_t *node;

		for (node = generators.list; ; node = node->next)
			if (node->next == generator)
			{
				node->next = generator->next;
				break;
			}
	}

	generators.count--;

	free(generator);
}

void link_generator (generator_t *generator)
{
	if (generators.list == NULL)
		generators.list = generator;
	else
	{
		generator_t *node;

		for (node = generators.list; ; node = node->next)
		{
			if (node->next == NULL)
			{
				node->next = generator;
				break;
			}
		}
	}

	generators.count++;
}

void generator_switch_to_array()
{
	if (generators.count == 0)
		return;

	{
		generator_t *array = NEW_ARRAY(generator_t, generators.count);
		int index = 0;
		generator_t *node;

		for (node = generators.list; node != NULL; )
		{
			generator_t *gen = &array[index++], *next = node->next;
			memcpy(gen, node, sizeof(generator_t));
			gen->next = NULL;

			DELETE(node);

			node = next;
		}

		generators.list = array;
	}
}

void generator_switch_to_linked_list()
{
	if (generators.count == 0)
		return;

	{
		generator_t **nodes = NEW_ARRAY(generator_t*, generators.count + 1);
		int i = 0;

		for (i = 0; i < generators.count; ++i)
			nodes[i] = NEW(generator_t);

		for (i = 0; i < generators.count; ++i)
		{
			memcpy(nodes[i], &generators.list[i], sizeof(generator_t));
			nodes[i]->next = nodes[i + 1];
		}

		DELETE_ARRAY(generators.list);
		generators.list = nodes[0];
		DELETE_ARRAY(nodes);
	}
}

void generator_toggle_editor()
{
	if (generators.inEditorMode)
	{
		generator_switch_to_array();
		generators.inEditorMode = false;
	}
	else
	{
		generator_switch_to_linked_list();
		generators.inEditorMode = true;
	}

	generators.selected = NULL;
	Com_Printf("Generator editor mode %s\n", (generators.inEditorMode) ? "enabled" : "disabled");
}

void clear_generators()
{
	if (generators.inEditorMode)
	{
		generator_t *gen;

		for (gen = generators.list; gen != NULL; )
		{
			generator_t *next = gen->next;

			delete_generator(gen);

			gen = next;
		}
	}
	else
	{
		if (generators.list != NULL)
			free(generators.list);
	}

	generators.list = NULL;
	generators.count = 0;
}

//================================================================================================================
// Utility functions

pointList_t *ClosestPoint(generator_t *gen)
{
	pointList_t *closest = NULL, *point;
	float closest_dist = 99999999, dist;
	vec3_t temp;

	for (point = gen->points; point != NULL; point = point->next)
	{
		VectorSubtract(rg.renderView.viewOrigin, point->point, temp);
		dist = VectorLength(temp);

		if (dist < closest_dist)
		{
			closest_dist = dist;
			closest = point;
		}
	}

	VectorSubtract(rg.renderView.viewOrigin, gen->center, temp);
	dist = VectorLength(temp);
			
	if (dist < closest_dist)
		closest = NULL;

	return closest;
}

void ClosestLine(generator_t *gen, vec3_t outLineStart, vec3_t outLineEnd, pointList_t **outPoint1, pointList_t **outPoint2)
{
	vec3_t closestLineStart, closestLineEnd;
	pointList_t *closest = ClosestPoint(gen);

	if (closest != NULL)
	{
		float toStartLen, toEndLen;

		if (closest->prev == NULL)
			VectorCopy(gen->center, closestLineStart);
		else
			VectorCopy(closest->prev->point, closestLineStart);

		if (closest->next != NULL)
			VectorCopy(closest->next->point, closestLineEnd);
		else
			VectorSet(closestLineEnd, 9999, 9999, 9999);

		toStartLen = DistanceSquared(rg.renderView.viewOrigin, closestLineStart);
		toEndLen = DistanceSquared(rg.renderView.viewOrigin, closestLineEnd);

		if (toStartLen < toEndLen)
		{
			VectorCopy(closest->point, outLineStart);
			VectorCopy(closestLineStart, outLineEnd);

			*outPoint1 = closest->prev;
			*outPoint2 = closest;
		}
		else
		{
			VectorCopy(closest->point, outLineStart);
			VectorCopy(closestLineEnd, outLineEnd);

			*outPoint1 = closest;
			*outPoint2 = closest->next;
		}
	}
	else
	{
		if (gen->points != NULL)
		{
			VectorCopy(gen->center, outLineStart);
			VectorCopy(gen->points->point, outLineEnd);

			*outPoint1 = NULL;
			*outPoint2 = gen->points;
		}
	}
}

void GetRandomCenter (generator_t *generator, vec3_t out)
{
	int index;
	int count = 0;
	pointList_t *point = NULL;
	vec3_t start, end, mid;
	float frac;

	for (point = generator->points; point != NULL; point = point->next)
		count++;

	if (count == 0)
	{
		VectorCopy(generator->center, out);
		return;
	}

	index = rand()%count;
	count = 0;

	for (point = generator->points; point != NULL; point = point->next)
	{
		if (count == index)
			break;

		count++;
	}

	if (point->prev == NULL)
		VectorCopy(generator->center, start);
	else
		VectorCopy(point->prev->point, start);

	VectorCopy(point->point, end);

	frac = random();
	lerp(start, end, frac, mid);

	VectorCopy(mid, out);
}

generator_t *GetRandomGenerator(generator_t **ignore, int ignoreCount)
{
	int attempt = 0;

	if (generators.inEditorMode)
		return NULL;

	if (ignoreCount == 0)
		return &generators.list[rand() % generators.count];
	else
	{
		generator_t **tempArray = NEW_ARRAY(generator_t*, generators.count - ignoreCount);
		generator_t *picked;
		int i, j;
			
		for (i = 0, j = 0; i < generators.count; ++i)
		{
			int x = 0;
			bool ignoreMe = false;

			for (; x < ignoreCount; ++x)
				if (&generators.list[i] == ignore[x])
				{
					ignoreMe = true;
					break;
				}

			if (ignoreMe)
				continue;

			tempArray[j++] = &generators.list[i];
		}

		picked = tempArray[rand() % (generators.count - ignoreCount)];
		DELETE_ARRAY(tempArray);
		return picked;
	}
}

bool GeneratorTestCircleCircle (generator_t *circleGenerator, float circleRadius, vec3_t out)
{
	float rr = crand();
	float q = crand() * (M_PI * 2);
	float r = sqrtf(random());
	float x = (((circleGenerator->bcircle.radius - (circleRadius)) * r) * cosf(q));
	float y = (((circleGenerator->bcircle.radius - (circleRadius))) * sinf(q));

	if (circleGenerator->bcircle.radius < circleRadius)
		return false;

	GetRandomCenter(circleGenerator, out);
	out[0] += x;
	out[1] += y;

	return true;
}

bool GeneratorTestCylinderCylinder (generator_t *cylinderGenerator, float radius, float height, vec3_t out)
{
	float rr = crand();
	float q = crand() * (M_PI * 2);
	float r = sqrtf(random());
	float x = (((cylinderGenerator->bcircle.radius - (radius)) * r) * cosf(q));
	float y = (((cylinderGenerator->bcircle.radius - (radius))) * sinf(q));
	float z = crand() * ((cylinderGenerator->bcylinder.height / 2) - (height / 2));

	if (cylinderGenerator->bcylinder.radius < radius ||
		cylinderGenerator->bcylinder.height < height)
		return false;

	GetRandomCenter(cylinderGenerator, out);
	out[0] += x;
	out[1] += y;
	out[2] += z;

	return true;
}

bool GeneratorTestSquareSquare (generator_t *squareGenerator, vec2_t squareSize, vec3_t out)
{
	float x = crand() * ((squareGenerator->bsquare.size[0] / 2) - (squareSize[0] / 2));
	float y = crand() * ((squareGenerator->bsquare.size[1] / 2) - (squareSize[1] / 2));

	if (squareGenerator->bsquare.size[0] < squareSize[0] ||
		squareGenerator->bsquare.size[1] < squareSize[1])
		return false;

	GetRandomCenter(squareGenerator, out);
	out[0] += x;
	out[1] += y;

	return true;
}

bool GeneratorTestBoxBox (generator_t *boxGenerator, vec3_t boxSize, vec3_t out)
{
	float x = crand() * ((boxGenerator->bsquare.size[0] / 2) - (boxSize[0] / 2));
	float y = crand() * ((boxGenerator->bsquare.size[1] / 2) - (boxSize[1] / 2));
	float z = crand() * ((boxGenerator->bsquare.size[2] / 2) - (boxSize[2] / 2));
	
	if (boxGenerator->bbox.size[0] < boxSize[0] ||
		boxGenerator->bbox.size[1] < boxSize[1] ||
		boxGenerator->bbox.size[2] < boxSize[2])
		return false;

	GetRandomCenter(boxGenerator, out);
	out[0] += x;
	out[1] += y;
	out[2] += z;

	return true;
}

static vec3_t downVec = {0, 0, -1};
static vec3_t upVec = {0, 0, 1};

void PlaceOnGround(vec3_t val, vec3_t mins, vec3_t maxs)
{
	vec3_t downVal;
	vec3_t realMins = {mins[0], mins[1], 0};
	vec3_t realMaxs = {maxs[0], maxs[1], 0};
	VectorMA(val, 8192, downVec, downVal);

	trace_t downTrace = CM_BoxTrace(val, downVal, realMins, realMaxs, 0, MASK_SOLID);

	VectorCopy(downTrace.endpos, val);
	val[2] += (-mins[2]);
}

void PlaceInSky(vec3_t val, vec3_t mins, vec3_t maxs)
{
	vec3_t downVal, upVal;
	vec3_t realMins = {mins[0], mins[1], 0};
	vec3_t realMaxs = {maxs[0], maxs[1], 0};
	VectorMA(val, 8192, downVec, downVal);
	VectorMA(val, 8192, upVec, upVal);

	trace_t downTrace = CM_BoxTrace(val, downVal, realMins, realMaxs, 0, MASK_SOLID);
	trace_t upTrace = CM_BoxTrace(val, upVal, realMins, realMaxs, 0, MASK_SOLID);

	VectorCopy(downTrace.endpos, downVal);
	VectorCopy(upTrace.endpos, upVal);

	downVal[2] += (-mins[2]);
	upVal[2] -= maxs[2];

	val[2] = downVal[2] + (frand() * (upVal[2] - downVal[2]));
}

bool GeneratorsValid()
{
	return generators.count != 0;
}

bool TestGeneratorBBox(vec3_t mins, vec3_t maxs, GeneratorSpawnFlags flags, vec3_t out)
{
	static int ignoreCount;
	static generator_t **ignores;
	bool passed = false;

	ignoreCount = 0;
	ignores = NEW_ARRAY(generator_t*, generators.count);

	while (ignoreCount != generators.count)
	{
		generator_t *picked = (generators.inEditorMode) ? generators.selected : GetRandomGenerator(ignores, ignoreCount);
		vec3_t size = {-mins[0] + maxs[0], -mins[1] + maxs[1], -mins[2] + maxs[2]};
		float radius;

		if (size[0] > size[1])
			radius = size[0] / 2;
		else
			radius = size[1] / 2;
		
		switch (picked->type)
		{
		case GT_Circle:
			passed = GeneratorTestCircleCircle(picked, radius, out);
			break;
		case GT_Cylinder:
			passed = GeneratorTestCylinderCylinder(picked, radius, size[2], out);
			break;
		case GT_Box:
			passed = GeneratorTestBoxBox(picked, size, out);
			break;
		case GT_Square:
			passed = GeneratorTestSquareSquare(picked, size, out);
			break;
		}

		if (passed)	
		{
			if (flags & Monster_Ground)
				PlaceOnGround(out, mins, maxs);
			else
			{
				switch (picked->type)
				{
				case GT_Box:
				case GT_Cylinder:
					out[2] -= (maxs[2] - -mins[2]) / 2;
					break;

				// TODO
				case GT_Square:
				case GT_Circle:
					PlaceInSky(out, mins, maxs);
					//out[2] += -mins[2];
					break;
				}
				break;
			}
		}

		if (generators.inEditorMode)
			break;

		ignores[ignoreCount] = picked;
		ignoreCount++;
	}

	DELETE_ARRAY(ignores);

	return passed;
}

//================================================================================================================
// Drawing functions

void Square(vec3_t center, vec2_t size)
{
	float halfWidth = size[0] / 2;
	float halfLength = size[1] / 2;

	qglBegin(GL_LINE_LOOP);
	qglVertex3f(center[0] + halfWidth, center[1] + halfLength, center[2]);
	qglVertex3f(center[0] + halfWidth, center[1] - halfLength, center[2]);
	qglVertex3f(center[0] - halfWidth, center[1] - halfLength, center[2]);
	qglVertex3f(center[0] - halfWidth, center[1] + halfLength, center[2]);
	qglEnd();
}

void SquareLine(generator_t *square, vec3_t p1, vec3_t p2)
{
	float halfWidth = square->bsquare.size[0] / 2;
	float halfLength = square->bsquare.size[1] / 2;
	
	qglBegin(GL_LINES);
	qglVertex3f(p1[0] - halfWidth, p1[1] - halfLength, p1[2]);
	qglVertex3f(p2[0] - halfWidth, p2[1] - halfLength, p2[2]);

	qglVertex3f(p1[0] + halfWidth, p1[1] + halfLength, p1[2]);
	qglVertex3f(p2[0] + halfWidth, p2[1] + halfLength, p2[2]);

	qglVertex3f(p1[0] + halfWidth, p1[1] - halfLength, p1[2]);
	qglVertex3f(p2[0] + halfWidth, p2[1] - halfLength, p2[2]);

	qglVertex3f(p1[0] - halfWidth, p1[1] + halfLength, p1[2]);
	qglVertex3f(p2[0] - halfWidth, p2[1] + halfLength, p2[2]);
	qglEnd();
}

void Box(vec3_t center, vec3_t size)
{
	vec3_t downCenter = {center[0], center[1], center[2] - (size[2] / 2)};
	vec3_t upCenter = {center[0], center[1], center[2] + (size[2] / 2)};
	vec2_t squareSize = {size[0], size[1]};
	float halfWidth = size[0] / 2;
	float halfLength = size[1] / 2;

	Square(downCenter, squareSize);
	Square(upCenter, squareSize);

	qglBegin(GL_LINES);
	qglVertex3f(center[0] + halfWidth, center[1] + halfLength, downCenter[2]);
	qglVertex3f(upCenter[0] + halfWidth, upCenter[1] + halfLength, upCenter[2]);

	qglVertex3f(center[0] + halfWidth, center[1] - halfLength, downCenter[2]);
	qglVertex3f(upCenter[0] + halfWidth, upCenter[1] - halfLength, upCenter[2]);

	qglVertex3f(center[0] - halfWidth, center[1] - halfLength, downCenter[2]);
	qglVertex3f(upCenter[0] - halfWidth, upCenter[1] - halfLength, upCenter[2]);

	qglVertex3f(center[0] - halfWidth, center[1] + halfLength, downCenter[2]);
	qglVertex3f(upCenter[0] - halfWidth, upCenter[1] + halfLength, upCenter[2]);
	qglEnd();
}

void BoxLine(generator_t *box, vec3_t p1, vec3_t p2)
{
	float halfWidth = box->bsquare.size[0] / 2;
	float halfLength = box->bsquare.size[1] / 2;
	float halfHeight = box->bsquare.size[2] / 2;
	
	qglBegin(GL_LINES);
	qglVertex3f(p1[0] - halfWidth, p1[1] - halfLength, p1[2] - halfHeight);
	qglVertex3f(p2[0] - halfWidth, p2[1] - halfLength, p2[2] - halfHeight);

	qglVertex3f(p1[0] + halfWidth, p1[1] + halfLength, p1[2] - halfHeight);
	qglVertex3f(p2[0] + halfWidth, p2[1] + halfLength, p2[2] - halfHeight);

	qglVertex3f(p1[0] + halfWidth, p1[1] - halfLength, p1[2] - halfHeight);
	qglVertex3f(p2[0] + halfWidth, p2[1] - halfLength, p2[2] - halfHeight);

	qglVertex3f(p1[0] - halfWidth, p1[1] + halfLength, p1[2] - halfHeight);
	qglVertex3f(p2[0] - halfWidth, p2[1] + halfLength, p2[2] - halfHeight);

	qglVertex3f(p1[0] - halfWidth, p1[1] - halfLength, p1[2] + halfHeight);
	qglVertex3f(p2[0] - halfWidth, p2[1] - halfLength, p2[2] + halfHeight);

	qglVertex3f(p1[0] + halfWidth, p1[1] + halfLength, p1[2] + halfHeight);
	qglVertex3f(p2[0] + halfWidth, p2[1] + halfLength, p2[2] + halfHeight);

	qglVertex3f(p1[0] + halfWidth, p1[1] - halfLength, p1[2] + halfHeight);
	qglVertex3f(p2[0] + halfWidth, p2[1] - halfLength, p2[2] + halfHeight);

	qglVertex3f(p1[0] - halfWidth, p1[1] + halfLength, p1[2] + halfHeight);
	qglVertex3f(p2[0] - halfWidth, p2[1] + halfLength, p2[2] + halfHeight);
	qglEnd();
}

void Circle(vec3_t center, float radius)
{      
	float da = radius / 2;
	int i = 0;

	if (da < 20)
		da = 20;

    qglBegin(GL_LINE_LOOP); 
    for(i = 0; i < da; i++)
	{ 
        float angle = i*2*M_PI/da; 
        qglVertex3f(center[0] + (cos(angle) * radius), center[1] + (sin(angle) * radius), center[2]); 
    } 
    qglEnd();
}

void CircleLine(generator_t *circle, vec3_t point1, vec3_t point2)
{
	vec3_t vec;
	vec3_t forward, right;
	vec3_t zlessP1, zlessP2;
	vec3_t angles;

	VectorSet(zlessP1, point1[0], point1[1], 0);
	VectorSet(zlessP2, point2[0], point2[1], 0);
	
	VectorSubtract(zlessP1, zlessP2, vec);
	VecToAngles(vec, angles);

	AngleVectors(angles, forward, right, NULL);

	{
		vec3_t temp1, temp2;
		VectorMA(point1, circle->bcircle.radius, right, temp1);
		VectorMA(point2, circle->bcircle.radius, right, temp2);

		temp1[2] = point1[2];
		temp2[2] = point2[2];

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		VectorMA(point1, -circle->bcircle.radius, right, temp1);
		VectorMA(point2, -circle->bcircle.radius, right, temp2);

		temp1[2] = point1[2];
		temp2[2] = point2[2];

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();


		VectorMA(point1, circle->bcircle.radius, forward, temp1);
		VectorMA(point2, circle->bcircle.radius, forward, temp2);

		temp1[2] = point1[2];
		temp2[2] = point2[2];

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		VectorMA(point1, -circle->bcircle.radius, forward, temp1);
		VectorMA(point2, -circle->bcircle.radius, forward, temp2);

		temp1[2] = point1[2];
		temp2[2] = point2[2];

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();
	}
}

void Cylinder(vec3_t center, float radius, float height)
{      
	float da = radius / 2;
	int i = 0;
	vec3_t upCenter = {center[0], center[1], center[2] + (height / 2)};
	vec3_t downCenter = {center[0], center[1], center[2] - (height / 2)};

	if (da < 20)
		da = 20;

	Circle(upCenter, radius);
	Circle(downCenter, radius);

    qglBegin(GL_LINES); 
    for(i = 0; i < da; i++)
	{ 
        float angle = i*2*M_PI/da; 
		float ax = (cos(angle) * radius);
		float ay = (sin(angle) * radius);

        qglVertex3f(center[0] + ax, center[1] + ay, downCenter[2]); 
        qglVertex3f(upCenter[0] + ax, upCenter[1] + ay, upCenter[2]); 
    } 
    qglEnd();
}

void CylinderLine(generator_t *cylinder, vec3_t point1, vec3_t point2)
{
	vec3_t vec;
	vec3_t forward, right;
	vec3_t zlessP1, zlessP2;
	vec3_t angles;

	VectorSet(zlessP1, point1[0], point1[1], 0);
	VectorSet(zlessP2, point2[0], point2[1], 0);
	
	VectorSubtract(zlessP1, zlessP2, vec);
	VecToAngles(vec, angles);

	AngleVectors(angles, forward, right, NULL);

	{
		vec3_t temp1, temp2;
		VectorMA(point1, cylinder->bcylinder.radius, right, temp1);
		VectorMA(point2, cylinder->bcylinder.radius, right, temp2);

		temp1[2] = point1[2] - (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] - (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		temp1[2] = point1[2] + (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] + (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		VectorMA(point1, -cylinder->bcylinder.radius, right, temp1);
		VectorMA(point2, -cylinder->bcylinder.radius, right, temp2);

		temp1[2] = point1[2] - (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] - (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		temp1[2] = point1[2] + (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] + (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

	
		VectorMA(point1, cylinder->bcylinder.radius, forward, temp1);
		VectorMA(point2, cylinder->bcylinder.radius, forward, temp2);

		temp1[2] = point1[2] - (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] - (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		temp1[2] = point1[2] + (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] + (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		VectorMA(point1, -cylinder->bcylinder.radius, forward, temp1);
		VectorMA(point2, -cylinder->bcylinder.radius, forward, temp2);

		temp1[2] = point1[2] - (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] - (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();

		temp1[2] = point1[2] + (cylinder->bcylinder.height / 2);
		temp2[2] = point2[2] + (cylinder->bcylinder.height / 2);

		qglBegin(GL_LINES);
		qglVertex3fv(temp1);
		qglVertex3fv(temp2);
		qglEnd();
	}
}

//================================================================================================================
// Commands

void R_Generators_EnableEdit(void)
{
	generator_toggle_editor();
}

void R_Generators_Add(void)
{
	generator_t *gen;

	if (Cmd_Argc() < 2)
	{
		Com_Printf("usage: gen_add {circle/square/box/cylinder}\n");
		return;
	}

	gen = NEW(generator_t);
	create_generator(gen);

	if (Q_stricmp(Cmd_Argv(1), "circle") == 0)
	{
		create_circle_generator(gen, rg.renderView.viewOrigin, 32);
		generators.selected = gen;
	}
	else if (Q_stricmp(Cmd_Argv(1), "square") == 0)
	{
		vec2_t size = {32, 32};
		create_square_generator(gen, rg.renderView.viewOrigin, size);
		generators.selected = gen;
	}
	else if (Q_stricmp(Cmd_Argv(1), "cylinder") == 0)
	{
		create_cylinder_generator(gen, rg.renderView.viewOrigin, 32, 32);
		generators.selected = gen;
	}
	else if (Q_stricmp(Cmd_Argv(1), "box") == 0)
	{
		vec3_t size = {32, 32, 32};
		create_box_generator(gen, rg.renderView.viewOrigin, size);
		generators.selected = gen;
	}
	else
	{
		delete_generator(gen);
		free(gen);
		Com_Printf("invalid type\n");
	}
}

void R_Generators_Select_Next(void)
{
	if (generators.selected == NULL)
		generators.selected = generators.list;
	else
	{
		generators.selected = generators.selected->next;

		if (generators.selected == NULL)
			generators.selected = generators.list;
	}
}

void R_Generators_Select_Prev(void)
{
	if (generators.selected == NULL)
		generators.selected = generators.list;
	else
	{
		generator_t *node, *lastNode, *wasSelected = generators.selected;
		bool found = false;
		
		generators.selected = NULL;

		for (node = generators.list; node != NULL; node = node->next)
		{
			if (node->next == wasSelected)
			{
				wasSelected = node;
				found = true;
			}

			if (node->next == NULL)
				lastNode = node;
		}

		if (found == false)
			generators.selected = lastNode;
		else
			generators.selected = wasSelected;
	}
}

enum GT_OpType
{
	GT_Type_None,
	GT_Type_Set,
	GT_Type_Add,
	GT_Type_Subtract,
	GT_Type_Divide,
	GT_Type_Multiply
};

enum GT_OpOperand
{
	GT_Operand_None,
	GT_Operand_Origin,

	// circles
	GT_Operand_Radius,

	// boxes
	GT_Operand_Width,
	GT_Operand_Length,

	// both
	GT_Operand_Height
};

void R_Generators_Set(void)
{
	GT_OpType type;
	GT_OpOperand operand;

	if (Cmd_Argc() < 2)
	{
		Com_Printf ("usage: gen_set {origin/radius/width/length/height} [value]\n you can also apply operators (a = add, s = sub, d = div, m = mul) to the value, such as +5 to add 5 to the current value\nex: gen_set origin a5,0,0\nex: gen_set radius 24");
		return;
	}

	if (generators.selected == NULL)
		return;
	
	if (Q_stricmp(Cmd_Argv(1), "origin") == 0)
		operand = GT_Operand_Origin;
	else if (Q_stricmp(Cmd_Argv(1), "radius") == 0)
		operand = GT_Operand_Radius;
	else if (Q_stricmp(Cmd_Argv(1), "width") == 0)
		operand = GT_Operand_Width;
	else if (Q_stricmp(Cmd_Argv(1), "length") == 0)
		operand = GT_Operand_Length;
	else if (Q_stricmp(Cmd_Argv(1), "height") == 0)
		operand = GT_Operand_Height;

	if (Cmd_Argc() >= 3)
	{
		if (Cmd_Argv(2)[0] == 'a')
			type = GT_Type_Add;
		else if (Cmd_Argv(2)[0] == 's')
			type = GT_Type_Subtract;
		else if (Cmd_Argv(2)[0] == 'm')
			type = GT_Type_Multiply;
		else if (Cmd_Argv(2)[0] == 'd')
			type = GT_Type_Divide;
		else
			type = GT_Type_Set;
	}
	else
		type = GT_Type_None;

	if (operand == GT_Operand_Origin)
	{
		int start = 0;
		char *value = Cmd_Argv(2) + ((type == GT_Type_Set) ? 0 : 1);
		vec3_t vecValue;
		char scratch[64];
		
		char *comma = strchr(value, ',');

		int num = comma - value;
		
		if (type == GT_Type_None)
		{
			VectorCopyRound(rg.renderView.viewOrigin, generators.selected->center);
			return;
		}

		Q_strncpyz(scratch, value, num + 1);

		vecValue[0] = atof(scratch);

		start = num + 1;

		comma = strchr(value + start, ',');

		num = comma - (value + start);

		Q_strncpyz(scratch, comma - num, num + 1);

		vecValue[1] = atof(scratch);

		start += num + 1;

		num = (value + strlen(value) - 1) - comma;

		Q_strncpyz(scratch, comma + 1, num + 1);

		vecValue[2] = atof(scratch);

		switch (type)
		{
		case GT_Type_Add:
			VectorAdd(generators.selected->center, vecValue, generators.selected->center);
			break;
		case GT_Type_Subtract:
			VectorSubtract(generators.selected->center, vecValue, generators.selected->center);
			break;
		case GT_Type_Multiply:
			generators.selected->center[0] *= vecValue[0];
			generators.selected->center[1] *= vecValue[1];
			generators.selected->center[2] *= vecValue[2];
			break;
		case GT_Type_Divide:
			generators.selected->center[0] /= vecValue[0];
			generators.selected->center[1] /= vecValue[1];
			generators.selected->center[2] /= vecValue[2];
			break;
		case GT_Type_Set:
			VectorCopy(vecValue, generators.selected->center);
			break;
		}
	}
	else
	{
		float value = (type == GT_Type_Set) ? atof(Cmd_Argv(2)) : atof(Cmd_Argv(2) + 1);
		float *ptr = NULL;

		switch (generators.selected->type)
		{
		case GT_Cylinder:
			{
				switch (operand)
				{
				case GT_Operand_Length:
				case GT_Operand_Width:
				case GT_Operand_Radius:
					ptr = &generators.selected->bcylinder.radius;
					break;
				case GT_Operand_Height:
					ptr = &generators.selected->bcylinder.height;
					break;
				}
			}
			break;
		case GT_Circle:
			{
				switch (operand)
				{
				case GT_Operand_Length:
				case GT_Operand_Width:
				case GT_Operand_Radius:
					ptr = &generators.selected->bcircle.radius;
					break;
				}
			}
			break;
		case GT_Square:
			{
				switch (operand)
				{
				case GT_Operand_Width:
					ptr = &generators.selected->bsquare.size[0];
					break;
				case GT_Operand_Length:
					ptr = &generators.selected->bsquare.size[1];
					break;
				}
			}
			break;
		case GT_Box:
			{
				switch (operand)
				{
				case GT_Operand_Width:
					ptr = &generators.selected->bbox.size[0];
					break;
				case GT_Operand_Length:
					ptr = &generators.selected->bbox.size[1];
					break;
				case GT_Operand_Height:
					ptr = &generators.selected->bbox.size[2];
					break;
				}
			}
			break;
		}

		if (ptr == NULL)
		{
			Com_Printf("Operand not supported with this type\n");
			return;
		}

		switch (type)
		{
		case GT_Type_Set:
			*ptr = value;
			break;
		case GT_Type_Add:
			*ptr += value;
			break;
		case GT_Type_Subtract:
			*ptr -= value;
			break;
		case GT_Type_Multiply:
			*ptr *= value;
			break;
		case GT_Type_Divide:
			*ptr /= value;
			break;
		}
	}
}

void R_Generators_Get(void)
{
	GT_OpOperand operand;
	if (Cmd_Argc() < 2)
	{
		Com_Printf ("usage: gen_get {origin/radius/width/length/height}\n");
		return;
	}

	if (generators.selected == NULL)
		return;
	
	if (Q_stricmp(Cmd_Argv(1), "origin") == 0)
		operand = GT_Operand_Origin;
	else if (Q_stricmp(Cmd_Argv(1), "radius") == 0)
		operand = GT_Operand_Radius;
	else if (Q_stricmp(Cmd_Argv(1), "width") == 0)
		operand = GT_Operand_Width;
	else if (Q_stricmp(Cmd_Argv(1), "length") == 0)
		operand = GT_Operand_Length;
	else if (Q_stricmp(Cmd_Argv(1), "height") == 0)
		operand = GT_Operand_Height;
	else
	{
		Com_Printf("unknown operand");
		return;
	}

	switch (operand)
	{
	case GT_Operand_Origin:
		Com_Printf ("origin: %f %f %f\n", generators.selected->center[0], generators.selected->center[1], generators.selected->center[2]);
		return;
	case GT_Operand_Height:
		switch (generators.selected->type)
		{
		case GT_Box:
			Com_Printf("height: %f\n", generators.selected->bbox.size[2]);
			return;
		case GT_Cylinder:
			Com_Printf("height: %f\n", generators.selected->bcylinder.height);
			return;
		}
		return;
	case GT_Operand_Width:
		switch (generators.selected->type)
		{
		case GT_Box:
			Com_Printf("width: %f\n", generators.selected->bbox.size[0]);
			return;
		case GT_Square:
			Com_Printf("width: %f\n", generators.selected->bsquare.size[0]);
			return;
		case GT_Cylinder:
			Com_Printf("radius: %f\n", generators.selected->bcylinder.radius);
			return;
		case GT_Circle:
			Com_Printf("radius: %f\n", generators.selected->bcircle.radius);
			return;
		}
		return;
	case GT_Operand_Length:
		switch (generators.selected->type)
		{
		case GT_Box:
			Com_Printf("length: %f\n", generators.selected->bbox.size[1]);
			return;
		case GT_Square:
			Com_Printf("length: %f\n", generators.selected->bsquare.size[1]);
			return;
		case GT_Cylinder:
			Com_Printf("radius: %f\n", generators.selected->bcylinder.radius);
			return;
		case GT_Circle:
			Com_Printf("radius: %f\n", generators.selected->bcircle.radius);
			return;
		}
		return;
	case GT_Operand_Radius:
		switch (generators.selected->type)
		{
		case GT_Cylinder:
			Com_Printf("radius: %f\n", generators.selected->bcylinder.radius);
			return;
		case GT_Circle:
			Com_Printf("radius: %f\n", generators.selected->bcircle.radius);
			return;
		}
	}

	Com_Printf("Shape doesn't support this operand\n");
}

void R_Generators_Convert(void)
{
	if (Cmd_Argc() < 2)
	{
		Com_Printf("usage: gen_convert {circle/square/box/cylinder}\n");
		return;
	}

	if (generators.selected == NULL)
		return;
	
	if (Q_stricmp(Cmd_Argv(1), "circle") == 0)
		generators.selected->type = GT_Circle;
	else if (Q_stricmp(Cmd_Argv(1), "square") == 0)
		generators.selected->type = GT_Square;
	else if (Q_stricmp(Cmd_Argv(1), "cylinder") == 0)
		generators.selected->type = GT_Cylinder;
	else if (Q_stricmp(Cmd_Argv(1), "box") == 0)
		generators.selected->type = GT_Box;
	else
		Com_Printf("invalid type\n");
}

void R_Generators_Delete(void)
{
	generator_t *prev = NULL;
	if (generators.selected == NULL)
		return;

	for (prev = generators.list; prev != NULL; prev = prev->next)
		if (prev->next == generators.selected)
			break;

	delete_generator(generators.selected);

	if (prev == NULL)
		generators.selected = generators.list;
	else
		generators.selected = prev;
}

void R_Generators_Points_Add(void)
{
	if (generators.selected == NULL)
		return;

	if (Cmd_Argc() > 1 && Q_stricmp(Cmd_Argv(1), "splice") == 0)
	{
		vec3_t dummy;
		pointList_t *outStart, *outEnd;

		if (generators.selected->points == NULL)
		{
			Com_Printf("Need a point to splice. Derp.\n");
			return;
		}

		ClosestLine(generators.selected, dummy, dummy, &outStart, &outEnd);

		if (outStart == NULL && outEnd != NULL)
		{
			generators.selected->points = NEW(pointList_t);
			VectorCopyRound(rg.renderView.viewOrigin, generators.selected->points->point);
			generators.selected->points->next = outEnd;
			generators.selected->points->prev = NULL;
			outEnd->prev = generators.selected->points;
		}
		else if (outEnd == NULL)
			return;
		else
		{
			pointList_t *newPoint = NEW(pointList_t);
			VectorCopyRound(rg.renderView.viewOrigin, newPoint->point);
			newPoint->next = outEnd;
			newPoint->prev = outStart;

			outStart->next = newPoint;
			outEnd->prev = newPoint;
		}
	}
	else
	{
		if (generators.selected->points == NULL)
		{
			generators.selected->points = NEW(pointList_t);
			VectorCopyRound(rg.renderView.viewOrigin, generators.selected->points->point);
			generators.selected->points->next = generators.selected->points->prev = NULL;
		}
		else
		{
			pointList_t *point = generators.selected->points;

			for (; ; point = point->next)
			{
				if (point->next == NULL)
				{
					point->next = NEW(pointList_t);
					point->next->next = NULL;
					point->next->prev = point;
					VectorCopyRound(rg.renderView.viewOrigin, point->next->point);				
					break;
				}
			}
		}
	}
}

void R_Generators_Points_Delete(void)
{
	pointList_t *closest;

	if (generators.selected == NULL)
		return;

	closest = ClosestPoint(generators.selected);

	if (closest != NULL)
	{
		if (closest->prev != NULL)
			closest->prev->next = closest->next;

		if (closest->next != NULL)
			closest->next->prev = closest->prev;

		if (generators.selected->points == closest)
			generators.selected->points = closest->next;

		free(closest);
	}
}

void R_Generators_Points_Set(void)
{
	if (generators.selected == NULL)
		return;

	if (Cmd_Argc() <= 1)
	{
		Com_Printf ("usage: gen_point_set 5 5 5\n");
		return;
	}

	{
	float x = atof(Cmd_Argv(1)), y = atof(Cmd_Argv(2)), z = atof(Cmd_Argv(3));
	pointList_t *closest = ClosestPoint(generators.selected);

	if (closest != NULL)
		VectorSet(closest->point, x, y, z);	
	}
}

void R_Generators_Points_Get(void)
{
	if (generators.selected == NULL)
		return;
	{
		pointList_t *closest = ClosestPoint(generators.selected);

		if (closest != NULL)
			Com_Printf("point origin: %f %f %f\n", closest->point[0], closest->point[1], closest->point[2]);
	}
}

void R_Generators_Save(void)
{
	char name[512];
	fileHandle_t f;

	if (sv.name == '\0')
		return;

	sprintf(name, "maps/%s.gen", sv.name);

	FS_CreatePath(name);
	FS_OpenFile( name, FS_WRITE, &f);

	{
		generator_t *gen;
		byte zero = 0, one = 1;
		
		for (gen = generators.list; gen != NULL; gen = gen->next)
		{
			pointList_t *point;

			FS_Write2(f, &gen->type, sizeof(gen->type));
			FS_Write2(f, gen->center, sizeof(float) * 3);

			FS_Write2(f, &gen->bsquare, sizeof(float) * 3);

			for (point = gen->points; point != NULL; point = point->next)
			{
				FS_Write2(f, &one, sizeof(byte));
				FS_Write2(f, point->point, sizeof(float) * 3);
			}

			FS_Write2(f, &zero, sizeof(byte));
		}

		FS_Write2(f, &zero, sizeof(byte));
	}

	FS_CloseFile(f);
}

void R_Generators_Load(void)
{
	char name[512];
	int opened;
	fileHandle_t f;

	if (sv.name == '\0')
		return;

	delete_generatorList(&generators);

	sprintf(name, "maps/%s.gen", sv.name);

	opened = FS_OpenFile( name, FS_READ, &f);
	
	if (opened == -1)
		return;

	{
		byte zero = 0, one = 1;

		generator_t *oldGenerator = NULL;
		while (true)
		{
			generatorType type;
			generator_t *generator;
			pointList_t *point = NULL;
			byte pointExists;

			FS_Read2(f, &type, sizeof(type));

			if (type == 0)
				break;

			generator = NEW(generator_t);

			if (oldGenerator != NULL)
				oldGenerator->next = generator;

			oldGenerator = generator;

			if (generators.list == NULL)
				generators.list = generator;

			generator->next = NULL;
			generator->type = type;
			generators.count++;
			FS_Read2(f, generator->center, sizeof(float) * 3);

			FS_Read2(f, &generator->bsquare, sizeof(float) * 3);

			while (true)
			{
				pointList_t *newPoint;

				FS_Read2(f, &pointExists, sizeof(byte));

				if (!pointExists)
					break;

				newPoint = NEW(pointList_t);
				FS_Read2(f, newPoint->point, sizeof(float) * 3);

				if (point == NULL)
				{
					point = newPoint;
					point->next = point->prev = NULL;
					generator->points = point;
				}
				else
				{
					while (point->next != NULL)
						point = point->next;

					newPoint->prev = point;
					newPoint->next = NULL;
					point->next = newPoint;
				}
			}
		}
	}

	generator_switch_to_array();
	FS_CloseFile(f);
}

void R_InitGeneratorEditor()
{	
	Cmd_AddCommand("gen_edit", R_Generators_EnableEdit);
	Cmd_AddCommand("gen_add", R_Generators_Add);
	Cmd_AddCommand("gen_next", R_Generators_Select_Next);
	Cmd_AddCommand("gen_prev", R_Generators_Select_Prev);
	Cmd_AddCommand("gen_set", R_Generators_Set);
	Cmd_AddCommand("gen_get", R_Generators_Get);
	Cmd_AddCommand("gen_convert", R_Generators_Convert);
	Cmd_AddCommand("gen_delete", R_Generators_Delete);
	Cmd_AddCommand("gen_point_add", R_Generators_Points_Add);
	Cmd_AddCommand("gen_point_delete", R_Generators_Points_Delete);
	Cmd_AddCommand("gen_point_set", R_Generators_Points_Set);
	Cmd_AddCommand("gen_point_get", R_Generators_Points_Get);

	Cmd_AddCommand("gen_save", R_Generators_Save);
	Cmd_AddCommand("gen_load", R_Generators_Load);

	init_generatorList(&generators);
}

void R_ShutdownGeneratorEditor()
{
	delete_generatorList(&generators);

	Cmd_RemoveCommand("gen_edit");
	Cmd_RemoveCommand("gen_add");
	Cmd_RemoveCommand("gen_next");
	Cmd_RemoveCommand("gen_prev");
	Cmd_RemoveCommand("gen_set");
	Cmd_RemoveCommand("gen_get");
	Cmd_RemoveCommand("gen_convert");
	Cmd_RemoveCommand("gen_delete");
	Cmd_RemoveCommand("gen_point_add");
	Cmd_RemoveCommand("gen_point_delete");
	Cmd_RemoveCommand("gen_point_set");
	Cmd_RemoveCommand("gen_point_get");

	Cmd_RemoveCommand("gen_save");
	Cmd_RemoveCommand("gen_load");
}

static byte white[] = {255, 255, 255, 128};
static byte red[] = {255, 0, 0, 128};
static byte blue[] = {0, 0, 255, 128};
static byte green[] = {0, 255, 0, 128};

void R_Generators_Draw()
{
	generator_t *gen;

	if (!generators.inEditorMode)
		return;   // Not active

	// Set the GL state
	GL_LoadMatrix(GL_MODELVIEW, rg.renderViewParms.worldMatrix);

	GL_DisableTexture();
	GL_Disable(GL_CULL_FACE);
	GL_Disable(GL_POLYGON_OFFSET_FILL);
	GL_Disable(GL_ALPHA_TEST);

	GL_Enable(GL_DEPTH_TEST);
	GL_DepthFunc(GL_LEQUAL);

	GL_Disable(GL_STENCIL_TEST);

	GL_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
	GL_DepthMask(GL_FALSE);
	GL_StencilMask(255);

	GL_DepthRange(0.0f, 1.0f);

	qglLineWidth(2);

	for (gen = generators.list; gen != NULL; gen = gen->next)
	{
		if (gen != generators.selected)
			qglColor3ubv(white);
		else
			qglColor3ubv(red);

		switch (gen->type)
		{
		case GT_Cylinder:
			Cylinder(gen->center, gen->bcylinder.radius, gen->bcylinder.height);
			break;
		case GT_Circle:
			Circle(gen->center, gen->bcircle.radius);
			break;
		case GT_Square:
			Square(gen->center, gen->bsquare.size);	
			break;
		case GT_Box:
			Box(gen->center, gen->bbox.size);
			break;
		}

		if (gen != generators.selected)
			qglColor4ubv(white);
		else
			qglColor4ubv(red);

		if (gen->points != NULL)
		{
			pointList_t *point = gen->points;
			pointList_t *closest = (gen == generators.selected) ? ClosestPoint(gen) : NULL;

			qglBegin(GL_LINES);
			qglVertex3fv(gen->center);
			qglVertex3fv(point->point);
			qglEnd();
			
			if (gen == generators.selected)
			{
				switch (gen->type)
				{
				case GT_Circle:
					CircleLine(gen, gen->center, point->point);
					break;
				case GT_Cylinder:
					CylinderLine(gen, gen->center, point->point);
					break;
				case GT_Square:
					SquareLine(gen, gen->center, point->point);
					break;
				case GT_Box:
					BoxLine(gen, gen->center, point->point);
					break;
				}
			}

			for (; point != NULL; point = point->next)
			{
				if (point->next != NULL)
				{
					qglBegin(GL_LINES);
					qglVertex3fv(point->point);
					qglVertex3fv(point->next->point);
					qglEnd();

					if (gen == generators.selected)
					{
						switch (gen->type)
						{
						case GT_Circle:
							CircleLine(gen, point->point, point->next->point);
							break;
						case GT_Cylinder:
							CylinderLine(gen, point->point, point->next->point);
							break;
						case GT_Square:
							SquareLine(gen, point->point, point->next->point);
							break;
						case GT_Box:
							BoxLine(gen, point->point, point->next->point);
							break;
						}
					}
				}
			}

			for (point = gen->points; point != NULL; point = point->next)
			{
				if (point == closest)
					qglColor4ubv(blue);
				else if (generators.selected == gen)
					qglColor4ubv(red);
				else
					qglColor4ubv(white);

				qglBegin(GL_POINTS);
				qglVertex3fv(point->point);
				qglEnd();
			}
			
			if (gen == generators.selected)
			{
				qglColor4ubv(blue);

				if (gen->points != NULL && closest == NULL)
				{
					qglBegin(GL_POINTS);
					qglVertex3fv(gen->center);
					qglEnd();
				}

				{
					vec3_t outStart, outEnd;
					pointList_t *op1, *op2;
					ClosestLine(gen, outStart, outEnd, &op1, &op2);

					qglBegin(GL_LINES);
					qglVertex3fv(outStart);
					qglVertex3fv(outEnd);
					qglEnd();
				}
			}
		}
	}
}